<?php

declare (strict_types=1);

namespace app\common\service\user;

use app\common\exception\AdminException;
use app\common\repository\ad\RewardLogRepository;
use app\common\repository\user\UserRepository;
use app\common\repository\user\UserTokensRepository;
use app\common\service\ad\RewardLogService;
use app\common\service\BaseService;
use app\common\service\BSCSignatureService;
use app\common\service\SmsService;
use app\common\util\JwtUtil;
use think\facade\Db;
use think\facade\Log;
use think\facade\Request;
use think\facade\Cache;
use Zhuzhichao\IpLocationZh\Ip;
use GuzzleHttp\Client;
use Web3\Utils;
use kornrunner\Secp256k1;
use kornrunner\Keccak;
use kornrunner\Ethereum\Address;
use Agustind\Ethsignature;
use Web3p\EthereumUtil\Util;
use Elliptic\EC;
use think\facade\Config;

class UserService extends BaseService
{
    public function __construct(UserRepository $repository)
    {
        $this->repository = $repository;
        parent::__construct($repository);
    }

    /**
     * 获取用户列表
     *
     * @param array $params
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     */
    public function getList(array $params = []): array
    {
        [$page, $pageSize] = $this->getPaginationParams();
        $where = [];
        if (isset($params['username'])) {
            $where[] = ['username', 'like', '%' . $params['username'] . '%'];
        }
        if (isset($params['status']) && $params['status'] != -1) {
            $where[] = ['status', '=', $params['status']];
        }
        $data = $this->repository->paginate($where, ['id' => 'desc'], $page, $pageSize);

        foreach ($data['list'] as &$vo) {
            $vo['day_reward_count'] = Db::name('reward_log')->where('user_id', $vo['id'])->whereDay('create_time')->count();
            $vo['day_task_number'] = (int)get_config('app.day_task_number');
        }
        unset($vo);
        return $data;
    }

    /**
     * 获取用户详情信息
     *
     * @param $id
     * @return \app\common\model\BaseModel|array|mixed
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getInfo($id): mixed
    {
        $userInfo = $this->repository->getOne($id);
        if(!empty($userInfo['pid'])) {
            $userInfo->paddress = $this->repository->getOne($userInfo['pid'])->address;
            $userInfo->pinvite_code = $this->repository->getOne($userInfo['pid'])->invite_code;
        }
        return $userInfo;
    }

    /**
     * 创建用户信息
     *
     * @param array $data
     * @return \think\Collection|\app\common\model\BaseModel|array
     * @throws \Exception
     */
    public function create(array $data): \think\Collection|\app\common\model\BaseModel|array
    {
        if (!empty($params['password'])) {
            $data['password'] = password_hash($params['password'], PASSWORD_DEFAULT);
        }
        return $this->repository->save($data);
    }

    /**
     * 更新用户信息
     *
     * @param array $data
     * @param $id
     * @return \app\common\model\BaseModel|bool|int
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function update(array $data, $id)
    {
        if (empty($id)) {
            throw new AdminException('ID 不能为空');
        }
        if (!empty($data['password'])) {
            $data['password'] = password_hash($data['password'], PASSWORD_DEFAULT);
        }
        return $this->repository->update($data, $id);
    }

    /**
     * 删除用户信息
     *
     * @param $id
     * @return bool
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function delete($id): bool
    {
        return $this->repository->delete($id);
    }

    /**
     * 修改用户金币
     *
     * @param $user_id
     * @param $data
     * @return \app\common\model\BaseModel|bool|int
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function saveUserBalance($user_id, $data): \app\common\model\BaseModel|bool|int
    {
        $type = $data['type'];
        $number = (int)$data['number'];
        $userInfo = $this->repository->getOne($user_id);
        if ($userInfo['balance'] < $number && $type === 'dec') {
            throw new AdminException('修改金币失败：当前用户金币不能少于扣减金币数量');
        }
        if ($type === 'empty') {
            $result = $this->repository->update(['balance' => 0], $user_id);
        } else {
            $result = $this->repository->setInc($user_id, 'balance', $type, $number);
        }
        return $result;
    }

    /**
     * 退出登录
     *
     * @param $user_id
     * @param $token
     * @return \app\common\model\BaseModel|bool|int
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function logout($user_id, $token): \app\common\model\BaseModel|bool|int
    {
        $token = hash('sha256', $token);
        $userService = app()->make(UserTokensRepository::class);
        return $userService->update(['status' => 0], [
            'user_id' => $user_id,
            'token' => $token,
        ]);
    }

    /**
     * 获取登录挑战（nonce）
     *
     * @param $address
     * @return array
     */
    public function getNonce($address): array
    {
        if (empty($address)) {
            throw new AdminException('Address does not exist');
        }
        $address = strtolower($address);
        if (!preg_match('/^0x[a-fA-F0-9]{40}$/', $address)) {
            throw new AdminException('Invalid address');
        }

        // 生成nonce
        $nonce = BSCSignatureService::generateNonce($address);

        // 构建消息
        $message = BSCSignatureService::buildMessage($nonce, 'Welcome to our application');

        return [
            'nonce' => $nonce,
            'message' => $message,
            'address' => $address,
        ];
    }

    /**
     * bsc登录
     *
     * @param $data
     * @return void
     */
    public function bscLogin($data)
    {
        $parentInviteCode = $data['invite_code'] ?? '';
        // 输入验证
        $validate = \think\facade\Validate::rule([
            'address' => 'require|length:42', // 钱包地址
            'signature' => 'require|length:132', // 签名
            'message' => 'require', // 信息
        ]);
        if (!$validate->check($data)) {
            throw new AdminException($validate->getError(), 400);
        }

        // 验证地址格式
        if (!preg_match('/^0x[a-fA-F0-9]{40}$/', $data['address'])) {
            throw new AdminException('Invalid address');
        }

        $address = $data['address'];
        $signature = $data['signature'];
        $message = $data['message'];

        // 验证钱包签名
        $result = BSCSignatureService::completeVerify($address, $signature, $message);
        if($result['success']) {
            return $this->transaction(function () use ($address, $parentInviteCode) {
                // 用户数据处理
                $userInfo = $this->repository->getOne(['address' => $address]);
                if (!$userInfo) {
                    // 注册新用户
                    $ip = Request::ip();
                    $data = [
                        'address' => $address,
                        'ip' => $ip,
                        'status' => 1,
                    ];
                    // 生成自己的邀请码
                    $data['invite_code'] = generateRandomString(8);
                    $userInfo = $this->repository->save($data);
                    $userInfo->save([
                        'path' => ',' . $userInfo['id'] . ','
                    ]);
                    // 写入用户资产钱包表
                    app()->make(UserAccountService::class)->add([
                        'user_id' => $userInfo['id'],
                        'address' => $address,
                    ]);
                }

                // 生成JWT
                $token = JwtUtil::generateToken($userInfo->id);

                // 写入token表
                $userTokensService = app()->make(UserTokensService::class);
                $userTokensService->create([
                    'user_id' => $userInfo['id'],
                    'token' => hash('sha256', $token), // 存哈希
                    'expires_time' => time() + Config::get('jwt.expire'),
                    'status' => 1
                ]);

                return [
                    'address' => $address,
                    'token' => $token
                ];
            });
        } else {
            throw new AdminException($result['error']);
        }
    }

    /**
     * 绑定邀请人
     *
     * @param $user_id
     * @param $code
     * @return \app\common\model\BaseModel|bool|int
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function bindInviter($user_id, $code): \app\common\model\BaseModel|bool|int
    {
        $userInfo = $this->repository->getOne($user_id);

        // 禁止重复绑定
        if(!empty($userInfo['pid'])) {
            throw new AdminException('Cannot bind inviter repeatedly');
        }

        // 不能绑定自己
        if($userInfo['invite_code'] === $code) {
            throw new AdminException('The invitation code is incorrect');
        }

        // 查询邀请人信息
        $parentUserInfo = $this->repository->getOne([
            'invite_code' => $code,
            'status' => 1,
        ]);

        if(!$parentUserInfo) {
            throw new AdminException('Please enter the correct invitation code');
        }

        // 修改团队路径
        $path = $parentUserInfo['path'] . $user_id . ',';
        return $this->repository->update(['pid' => $parentUserInfo->id, 'path' => $path], [
            'id' => $user_id,
            'status' => 1,
        ]);
    }

    /**
     * 获取推荐人和团队信息
     *
     * @param $user_id
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     */
    public function getDirectTeamInfo($user_id): array
    {
        [$page, $pageSize] = $this->getPaginationParams();
        $where = ['pid' => $user_id];
        $data = $this->repository->paginate($where, ['create_time' => 'desc'], $page, $pageSize);
        foreach ($data['list'] as &$item) {
            $teamIds = Db::name('user')->where('id', '<>', $item['id'])->where('path', 'like', '%,' . $item['id'] . ',%')->column('id');
            $item['personalTotalPoa'] = Db::name('user_investment')->where('status', 1)->where('user_id', $item['id'])->sum('usdt_amount'); // 个人poa
            $item['teamTotalPoa'] = Db::name('user_investment')->whereIn('user_id', $teamIds)->where('status', 1)->sum('usdt_amount'); // 团队poa总算里
            $item['teamTotalCastingPoa'] = Db::name('user_investment')->where('mode', 2)->where('status', 1)->whereIn('user_id', $teamIds)->sum('usdt_amount'); // 内容铸造算力
            $item['teamTotalLiquidityPoa'] = Db::name('user_investment')->where('mode', 1)->where('status', 1)->whereIn('user_id', $teamIds)->sum('usdt_amount'); // 流动性算力
        }
        return $data;
    }

    /**
     * 单独为指定用户执行等级升级逻辑
     *
     * @param $userId
     * @return array 包含执行结果和日志信息的数组
     * @throws \Exception
     */
    public function upgradeUserLevel($userId): array
    {
        try {
            // 1. 获取启用的等级配置（按等级升序）
            $levelConfigs = Db::name('user_level')
                ->where('status', 1)
                ->order('level', 'asc')
                ->select()
                ->toArray();

            $result['log'][] = "等级配置: " . json_encode($levelConfigs, JSON_UNESCAPED_UNICODE);

            if (empty($levelConfigs)) {
                $result['message'] = "未找到启用的等级配置！";
                $result['log'][] = "❌ {$result['message']}";
                return $result;
            }

            // 2. 获取目标用户信息
            $user = Db::name('user')
                ->where('id', $userId)
                ->where('status', 1)
                ->field('id, pid, path, level, invite_code')
                ->find();

            if (!$user) {
                $result['message'] = "用户不存在或已被禁用/删除！";
                $result['log'][] = "❌ {$result['message']}";
                return $result;
            }

            // === 1. 获取该用户所有下级用户 ID（通过 path 字段快速查询）===
            $subUserIds = $this->getSubordinateIdsByPath($user['id'], $user['path']);
            if (empty($subUserIds)) {
                return $result;
            }

            // === 2. 计算每个直推团队的 USDT 投资总额 ===
            $directUsers = Db::name('user')
                ->where('pid', $user['id'])
                ->column('id');

            if (empty($directUsers)) {
                $result['log'][] = "  🚫 无直推下级";
                $result['message'] = "无直推下级";
                return $result;
            }

            $teamUsdtList = [];
            $totalTeamUsdt = 0.0;

            foreach ($directUsers as $directId) {
                // 获取该直推子树的所有用户
                $subtreeIds = $this->getSubordinateIdsByPath($directId);
                $subtreeIds[] = $directId; // 包含自己

                $usdtSum = Db::name('user_investment')
                    ->whereIn('user_id', $subtreeIds)
                    ->where('status', 1) // 生效中
                    ->sum('usdt_amount') ?: 0.0;

                $teamUsdtList[] = $usdtSum;
                $totalTeamUsdt += $usdtSum;
            }

            // === 3. 计算小分区 USDT（去掉最大团队，即“大区”）===
            $bigZoneUsdt = max($teamUsdtList);
            $smallPartitionUsdt = $totalTeamUsdt - $bigZoneUsdt;
            $result['data']['small_partition_usdt'] = $smallPartitionUsdt;

            // === 4. 尝试升级（从低到高，取最高可达到等级）===
            $achievedLevel = $user['level'];

            foreach ($levelConfigs as $config) {
                if ($config['level'] <= $user['level']) continue;

                $requiredTotal = $config['total_mint'] ?? 0.0;
                $requiredSmall = $config['small_partition_mint'] ?? 0.0;

                if ($totalTeamUsdt >= $requiredTotal && $smallPartitionUsdt >= $requiredSmall) {
                    $achievedLevel = $config['level'];
                    $result['log'][] = "  ✅ 满足等级 {$config['level']} 条件！";
                } else {
                    $result['log'][] = "  🚫 不满足等级 {$config['level']}：需团队{$requiredTotal}/小分区{$requiredSmall}";
                    break; // 后续等级更高，不可能满足
                }
            }

            // === 5. 执行升级 ===
            if ($achievedLevel > $user['level']) {
                $updateData = [
                    'level' => $achievedLevel,
                    'upgrade_time' => time(),
                    'update_time' => time()
                ];

                $rows = Db::name('user')->where('id', $user['id'])->update($updateData);
                if ($rows !== false) {
                    $result['success'] = true;
                    $result['message'] = "成功升级至等级 {$achievedLevel}";
                    $result['data']['new_level'] = $achievedLevel;
                    $result['log'][] = "  🔺 成功升级至等级 {$achievedLevel}！";
                } else {
                    $result['message'] = "数据库更新失败";
                    $result['log'][] = "  ❌ 数据库更新失败";
                }
            } else {
                $result['success'] = true;
                $result['message'] = "当前等级已达标，无需升级";
                $result['log'][] = "  🟢 当前等级已达标，无需升级。";
            }

            $result['data']['processed'] = true;

            return $result;
        } catch (\Exception $e) {
            $result['message'] = "执行过程中发生异常: " . $e->getMessage();
            $result['log'][] = "  ❌ 异常: " . $e->getMessage();
            // 可以选择记录到日志文件
            // trace("UserUpgradeService Error: " . $e->getMessage(), 'error');
        }
    }

    /**
     * 通过 path 字段获取某用户的所有下级 ID（含多级）
     * path 格式: ,1,3,5,
     */
    private function getSubordinateIdsByPath($userId, ?string $path = null): array
    {
        if (!$path) {
            $path = ',' . $userId . ',';
        }

        return Db::name('user')
            ->where('path', 'like', $path . '%')
            ->where('id', '<>', $userId)
            ->column('id');
    }
}